#include "resourceloader.hh"

ResourceLoader::ResourceLoader() {
}

ResourceLoader::~ResourceLoader() {
}

QString ResourceLoader::executeQuery(QXmlQuery* queryPtr) {
  QString resultContainer;

  if (queryPtr->isValid()) {
    queryPtr->evaluateTo(&resultContainer);
  }
  else {
    //TODO Remplacer par exception
    std::cout << "requete invalide" << std::cout;
  }
  return resultContainer;
}

QStringList ResourceLoader::executeQueryMultipleResults(QXmlQuery* queryPtr) {
  QStringList resultContainer;

  if (queryPtr->isValid()) {
    queryPtr->evaluateTo(&resultContainer);
  }
  else {//TODO Remplacer par exception
    std::cout << "requete invalide" << std::cout;
  }
  return resultContainer;
}

void ResourceLoader::extractIsSavePoint(QXmlQuery* queryPtr, Room* room) {
  std::stringstream queryNotFormatted;
  queryNotFormatted << "doc($inputDocument)/data/rooms/room[@id='"<< room->getId() << "']/save/string()";
  std::string queryFormatted = queryNotFormatted.str();

  queryPtr->setQuery(QString(queryFormatted.c_str()));
  QStringList res = executeQueryMultipleResults(queryPtr);
  if (res.length() == 1) {
    if (res.at(0) == "0") {
      room->setIsSave(false);
    }
    else {
      room->setIsSave(true);
    }
  }
}

void ResourceLoader::extractAutoScene(Room* room, QXmlQuery* queryPtr) {
  std::stringstream queryNotFormatted;
  queryNotFormatted << "doc($inputDocument)/data/rooms/room[@id='"<< room->getId() << "']/auto_text_event_id/string()";
  std::string queryFormatted = queryNotFormatted.str();

  queryPtr->setQuery(QString(queryFormatted.c_str()));
  QStringList res = executeQueryMultipleResults(queryPtr);

  if (res.length() == 1) {
    std::stringstream autoStr(res.at(0).toLocal8Bit().constData());
    int tmp;
    autoStr >> tmp;
    room->setAutoTextEventId(tmp);
  }
}

QString ResourceLoader::extractBackgroundImgSrc(int roomId, QXmlQuery* queryPtr) {
  std::stringstream queryNotFormatted;
  queryNotFormatted << "doc($inputDocument)/data/rooms/room[@id='"<< roomId << "']/img_path/string()";
  std::string queryFormatted = queryNotFormatted.str();

  queryPtr->setQuery(QString(queryFormatted.c_str()));

  return executeQuery(queryPtr);
}

void ResourceLoader::extractElementsForRoom(QXmlQuery* queryPtr, Room* room) {
  std::stringstream queryNotFormattedX;
  std::stringstream queryNotFormattedY;
  std::stringstream queryNotFormattedName;
  std::stringstream queryNotFormattedImgPath;

  QStringList listResultX;
  QStringList listResultY;
  QStringList listResultName;
  QStringList listResultImgSrc;

  queryNotFormattedX << "doc($inputDocument)/data/elements/room[@id='"<< room->getId() << "']/element/x/string()";
  queryNotFormattedY << "doc($inputDocument)/data/elements/room[@id='"<< room->getId() << "']/element/y/string()";
  queryNotFormattedName << "doc($inputDocument)/data/elements/room[@id='"<< room->getId() << "']/element/name/string()";
  queryNotFormattedImgPath << "doc($inputDocument)/data/elements/room[@id='"<< room->getId() << "']/element/img_path/string()";

  /// Recuperation attribut X elements
  std::string queryFormatted = queryNotFormattedX.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultX = executeQueryMultipleResults(queryPtr);

  /// Recuperation attribut Y elements
  queryFormatted = "";
  queryFormatted = queryNotFormattedY.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultY = executeQueryMultipleResults(queryPtr);

  queryFormatted = "";
  queryFormatted = queryNotFormattedName.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultName = executeQueryMultipleResults(queryPtr);

  queryFormatted = "";
  queryFormatted = queryNotFormattedImgPath.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultImgSrc = executeQueryMultipleResults(queryPtr);

  for (int eltIndex = 0; eltIndex < listResultX.size(); eltIndex++) {
    Element* roomElement = 0;
    roomElement = new Element();

    std::stringstream xStr(listResultX.at(eltIndex).toLocal8Bit().constData());
    std::stringstream yStr(listResultY.at(eltIndex).toLocal8Bit().constData());

    int tmpVal;
    xStr >> tmpVal;
    roomElement->setX(tmpVal);
    tmpVal = 0;
    yStr >> tmpVal;
    roomElement->setY(tmpVal);
    roomElement->setName(listResultName.at(eltIndex).toStdString());
    roomElement->setImgSrc(":/resources/elements/" + listResultImgSrc.at(eltIndex).toStdString());

    /// Pour chaque element interactif on charge ses actions
    std::stringstream queryNotFormattedActionName;
    std::stringstream queryNotFormattedActionTextId;

    QStringList listResultActionName;
    QStringList listResultActionTextId;

    /// En XQuery/XPath, le premier element est en index 1
    queryNotFormattedActionName << "doc($inputDocument)/data/elements/room[@id='"
                                << room->getId()
                                << "']/element["
                                << eltIndex + 1
                                << "]/actions/action/name/string()";

    queryNotFormattedActionTextId << "doc($inputDocument)/data/elements/room[@id='"
                                  << room->getId()
                                  << "']/element["
                                  << eltIndex + 1
                                  << "]/actions/action/text_event_id/string()";

    queryFormatted = "";
    queryFormatted = queryNotFormattedActionName.str();
    queryPtr->setQuery(QString(queryFormatted.c_str()));
    listResultActionName = executeQueryMultipleResults(queryPtr);

    queryFormatted = "";
    queryFormatted = queryNotFormattedActionTextId.str();
    queryPtr->setQuery(QString(queryFormatted.c_str()));
    listResultActionTextId = executeQueryMultipleResults(queryPtr);

    for (int actId = 0; actId < listResultActionName.size(); actId++) {
      std::stringstream textIdStr(listResultActionTextId.at(actId).toLocal8Bit().constData());

      int tmp;
      textIdStr >> tmp;
      roomElement->addAction(listResultActionName.at(actId).toStdString(), tmp);
    }

    room->addElement(roomElement);
  }
}

void ResourceLoader::extractTextIdForRoom(QXmlQuery* queryPtr, Room* room) {
  std::stringstream queryNotFormattedId;
  std::stringstream queryNotFormattedLocutor;
  std::stringstream queryNotFormattedFace;
  std::stringstream queryNotFormattedText;
  std::stringstream queryNotFormattedNextEvtId;
  std::stringstream queryNotFormattedAddItemId;
  std::stringstream queryNotFormattedCnsItemId;
  std::stringstream queryNotFormattedChgRoomId;
  std::stringstream queryNotFormattedReqEvtId;

  QStringList listResultId;
  QStringList listResultLocutor;
  QStringList listResultFace;
  QStringList listResultText;
  QStringList listResultNextEvtId;
  QStringList listResultAddItemId;
  QStringList listResultCnsItemId;
  QStringList listResultChgRoomId;
  QStringList listResultReqEvtId;

  queryNotFormattedId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                    << room->getId()
                    << "']/text_event/id/string()";
  std::string queryFormatted = queryNotFormattedId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedLocutor << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/locutor/string()";
  queryFormatted = queryNotFormattedLocutor.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultLocutor = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedFace << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/face/string()";
  queryFormatted = queryNotFormattedFace.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultFace = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedText << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/text/string()";
  queryFormatted = queryNotFormattedText.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultText = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedNextEvtId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/next_event_id/string()";
  queryFormatted = queryNotFormattedNextEvtId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultNextEvtId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedAddItemId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/add_item_id/string()";
  queryFormatted = queryNotFormattedAddItemId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultAddItemId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedCnsItemId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/consume_item_id/string()";
  queryFormatted = queryNotFormattedCnsItemId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultCnsItemId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedChgRoomId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/change_room_id/string()";
  queryFormatted = queryNotFormattedChgRoomId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultChgRoomId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  queryNotFormattedReqEvtId << "doc($inputDocument)/data/text_events/rooms/room[@id='"
                           << room->getId()
                           << "']/text_event/require_text_event_id/string()";
  queryFormatted = queryNotFormattedReqEvtId.str();
  queryPtr->setQuery(QString(queryFormatted.c_str()));
  listResultReqEvtId = executeQueryMultipleResults(queryPtr);
  queryFormatted = "";

  for (int cpt = 0; cpt < listResultId.size(); cpt++) {
    TextEvent* roomTextEvent = 0;
    roomTextEvent = new TextEvent();

    int tmp;

    std::stringstream idStr(listResultId.at(cpt).toLocal8Bit().constData());
    std::stringstream nextEventIdStr(listResultNextEvtId.at(cpt).toLocal8Bit().constData());
    std::stringstream addItemIdStr(listResultAddItemId.at(cpt).toLocal8Bit().constData());
    std::stringstream cnsItemIdStr(listResultCnsItemId.at(cpt).toLocal8Bit().constData());
    std::stringstream chgRoomIdStr(listResultChgRoomId.at(cpt).toLocal8Bit().constData());
    std::stringstream reqEvtIdStr(listResultReqEvtId.at(cpt).toLocal8Bit().constData());

    idStr >> tmp;
    roomTextEvent->setId(tmp);

    roomTextEvent->setLocutor(listResultLocutor.at(cpt).toStdString());
    roomTextEvent->setText(listResultText.at(cpt).toStdString());
    roomTextEvent->setFace(listResultFace.at(cpt).toStdString());

    nextEventIdStr >> tmp;
    roomTextEvent->setNextEventId(tmp);

    addItemIdStr >> tmp;
    roomTextEvent->setAddItemId(tmp);

    cnsItemIdStr >> tmp;
    roomTextEvent->setConsumeItemId(tmp);

    chgRoomIdStr >> tmp;
    roomTextEvent->setNextRoomId(tmp);

    reqEvtIdStr >> tmp;
    roomTextEvent->setRequiredTextEventId(tmp);

    room->addTextEvent(roomTextEvent);
  }

}

Item ResourceLoader::extractItem(int idItemParam)
{
  QFile xml_file;
  xml_file.setFileName(":/resources/data/game_data.xml");

  if (!(xml_file.open(QIODevice::ReadOnly))) {
      std::cout << "Erreur ouverture fichier 4";
      exit(-9);
  }

  QXmlQuery query;
  query.bindVariable("inputDocument", &xml_file);

  std::stringstream queryNotFormattedName;
  std::stringstream queryNotFormattedImgSrc;
  std::stringstream queryNotFormattedActionName;
  std::stringstream queryNotFormattedActionTextId;

  /// /!\ On prend UN objet mais ici un QStringList car les resultats suite a un evaluateTo(QString) ajoutent un \n a la fin
  QStringList listResultName;
  QStringList listResultImgSrc;

  QStringList listResultActionName;
  QStringList listResultActionTextId;

  queryNotFormattedName << "doc($inputDocument)/data/items/item[@id='"
                        << idItemParam
                        << "']/name/string()";
  std::string queryFormatted = queryNotFormattedName.str();
  query.setQuery(QString(queryFormatted.c_str()));
  listResultName = executeQueryMultipleResults(&query);
  queryFormatted = "";

  queryNotFormattedImgSrc <<  "doc($inputDocument)/data/items/item[@id='"
                          << idItemParam
                          << "']/img_path/string()";
  queryFormatted = queryNotFormattedImgSrc.str();
  query.setQuery(QString(queryFormatted.c_str()));
  listResultImgSrc = executeQueryMultipleResults(&query);
  queryFormatted = "";

  queryNotFormattedActionName << "doc($inputDocument)/data/items/item[@id='"
                              << idItemParam
                              << "']/actions/action/name/string()";
  queryFormatted = queryNotFormattedActionName.str();
  query.setQuery(QString(queryFormatted.c_str()));
  listResultActionName = executeQueryMultipleResults(&query);
  queryFormatted = "";

  queryNotFormattedActionTextId << "doc($inputDocument)/data/items/item[@id='"
                                << idItemParam
                                << "']/actions/action/text_event_id/string()";
  queryFormatted = queryNotFormattedActionTextId.str();
  query.setQuery(QString(queryFormatted.c_str()));
  listResultActionTextId = executeQueryMultipleResults(&query);

  Item newItem;
  newItem.setId(idItemParam);
  newItem.setName(listResultName.at(0).toStdString());
  newItem.setImgSrc(":/resources/items/" + listResultImgSrc.at(0).toStdString());

  //boucle qui prend les actions
  for (int iItem = 0; iItem < listResultActionName.size(); iItem++) {
    std::stringstream textIdStr(listResultActionTextId.at(iItem).toLocal8Bit().constData());

    int tmp;
    textIdStr >> tmp;

    newItem.addAction(listResultActionName.at(iItem).toStdString(), tmp);
  }
  return newItem;
}

void ResourceLoader::loadRoom(Room* room) {
  /// Ouverture du fichier et setup classique
  QFile xml_file;

  // Toujours le problÃšme des path Windows
  QString path = qApp->applicationDirPath();

  QString filename = ":/resources/data/game_data.xml";
  //filename.prepend("/").prepend(path);
  xml_file.setFileName(filename);
  if (!(xml_file.open(QIODevice::ReadOnly))) {
    std::cout << "Erreur ouverture fichier 1" << std::endl;
    return;
  }

  /// La requete liee au document

  QXmlQuery query;
  query.bindVariable("inputDocument", &xml_file);

  QString img_fond = extractBackgroundImgSrc(room->getId(), &query);

  /// On extrait le contenu de la balise save pour savoir si on sauvegarde au debut de la salle
  extractIsSavePoint(&query, room);

  /// Enfin la salle recoit le path de son fond mais attention car un evaluateTo(QString) ajoute un \n
  room->setImgSrc(":/resources/scenes/"+ img_fond.toStdString().erase(img_fond.toStdString().find_first_of('\n')));

  /// On extrait l'evenement qui doit etre joue au debut de la scene
  extractAutoScene(room, &query);

  extractElementsForRoom(&query, room);
  extractTextIdForRoom(&query, room);
}


QList<QString> ResourceLoader::extractsave()
{
  // On va remplir cette liste avec les infos des sauvegardes (je pense qu'il faudra bientot y ajouter le num de la room

  QList<QString> savelist;

  QDir dir;
  QFile xml_file;
  QString filename = dir.homePath() + "/saveFormat.xml";
  xml_file.setFileName(filename);
  ////
  if (!(xml_file.open(QIODevice::ReadOnly))) {
      std::cout << "Erreur ouverture fichier 2";
      return savelist;
  }
  /// La requete liee au document

  QXmlQuery query;
  query.bindVariable("inputDocument", &xml_file);
  std::string idStr;
  std::stringstream oss;
  oss << "doc($inputDocument)/save/game/id | doc($inputDocument)/save/game/room | doc($inputDocument)/save/game/date | doc($inputDocument)/save/game/player/name";
  idStr = "";
  idStr = oss.str();

  QString request_results;

  query.setQuery(QString(idStr.c_str()));
  if (query.isValid()) {
    std::cout << "requete good";
    //    bool ret;
    query.evaluateTo(&request_results);
    //    std::cout << "retour " << ret;
  }
  else {
    std::cout << " requete invalide";
  }
  std::cout << std::endl;
  std::cout << request_results.toStdString() << std::endl;
  QStringList splitlist = request_results.split("\n");
  for (int i = 0; i < splitlist.count() - 1; i++)
  {
    QString gameid = splitlist.operator [](i);
    gameid.remove("<id>");
    gameid.remove("</id>");
    QString room = splitlist.operator [](++i);
    room.remove("<room>");
    room.remove("</room>");
    QString date = splitlist.operator [](++i);
    date.remove("<date>");
    date.remove("</date>");
    QString name = splitlist.operator [](++i);
    name.remove("<name>");
    name.remove("</name>");
    name.append(" - le ").append(date).append("^").append(gameid).append("%").append(room);
    std::cout << std::endl;
    std::cout << name.toStdString() << std::endl;
    savelist.push_back(name);
  }
  return savelist;
}

std::list<int> ResourceLoader::getPlayerItems(std::string id)
{
  std::list<int> itemidlist;

  QDir dir;
  QFile xml_file;
  QString filename = dir.homePath() + "/saveFormat.xml";
  xml_file.setFileName(filename);
  ////
  if (!(xml_file.open(QIODevice::ReadOnly))) {
      std::cout << "Erreur ouverture fichier 3";
      return itemidlist;
  }
  /// La requete liee au document

  QXmlQuery query;
  query.bindVariable("inputDocument", &xml_file);
  std::string idStr;
  std::stringstream oss;
  oss << "doc($inputDocument)/save/game[id='"<< id << "']/player/items/*";
  idStr = "";
  idStr = oss.str();

  QString request_results;

  query.setQuery(QString(idStr.c_str()));
  if (query.isValid()) {
    std::cout << "requete good";
    //    bool ret;
    query.evaluateTo(&request_results);
    //    std::cout << "retour " << ret;
  }
  else {
    std::cout << " requete invalide";
  }
  std::cout << request_results.toStdString() << std::endl;
  QStringList splitlist = request_results.split("\n");
  for (int i = 0; i < splitlist.count() - 1; i++)
  {
    QString itemid = splitlist.operator [](i);
    itemid.remove("<item>");
    itemid.remove("</item>");
    itemidlist.push_back(itemid.toInt());
  }
  return itemidlist;
}



// SAVING

bool ResourceLoader::SaveGame(std::string gameid, int roomid, std::string playername, std::vector<Item> itemlist)
{
  /// Ouverture du fichier et setup classique
  QDir dir;
  QFile xml_file;
  QString filename = dir.homePath() + "/saveFormat.xml";
  xml_file.setFileName(filename);
  dir.setPath(dir.homePath());
  std::cout << dir.absolutePath().toStdString() << std::endl;
  // A verifier que ca marche correctement : chez moi le mkdir fonctionne la premiere fois et renvoie false les autres, ce qui correspond a la creation
  //dir.mkdir("Bug_Odyssey");
  if (!(xml_file.open(QIODevice::ReadWrite))) {
      std::cout << "Erreur ouverture fichier sauvegarde";
      return false;
  }
  QDomDocument doc;
  QDomElement root;
  doc.setContent(&xml_file);
  // si le fichier est vide on ajoute le doctype et le noaud racine save
  if (xml_file.size() == 0)
  {
    //creer le fichier
    QDomNode doctype = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"iso-8859-1\"");
    doc.insertBefore(doctype, doc.firstChild());

    root = doc.createElement("save");
    doc.appendChild(root);
  }
  // sinon on recupere le noeud racine
  else
    root = doc.documentElement();
  QDomNode d_game = doc.createElement("game");

  QDomNode d_id = doc.createElement("id");
  //QDomText d_id_t = doc.createTextNode(gameid.c_str());
  QDomText d_id_t = doc.createTextNode(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
  d_id.appendChild(d_id_t);

  QDomNode d_room = doc.createElement("room");
  QDomText d_room_t = doc.createTextNode(QString::number(roomid));
  d_room.appendChild(d_room_t);

  QDomNode d_date = doc.createElement("date");
  QDomText d_date_t = doc.createTextNode(QDate::currentDate().toString("yyyy-MM-dd"));
  d_date.appendChild(d_date_t);


  QDomNode d_player = doc.createElement("player");

  QDomNode d_name = doc.createElement("name");
  QDomText d_name_t = doc.createTextNode(QString(playername.c_str()));
  d_name.appendChild(d_name_t);

  QDomNode d_items = doc.createElement("items");
  for (unsigned int i = 0; i < itemlist.size(); i++)
  {
    QDomNode d_item = doc.createElement("item");
    QDomText d_item_t = doc.createTextNode(QString::number(itemlist.operator [](i).getId()));
    d_item.appendChild(d_item_t);
    d_items.appendChild(d_item);
  }

  d_player.appendChild(d_name);
  d_player.appendChild(d_items);

  d_game.appendChild(d_id);
  d_game.appendChild(d_room);
  d_game.appendChild(d_date);
  d_game.appendChild(d_player);
  root.appendChild(d_game);

  QTextStream qts(&xml_file);
  qts.seek(0);
  qts << doc.toString();
  //doc.save();
  xml_file.close();
  return true;
}
